
#include "naryTree.h"
#include "fileHandling.h"
#include "hashtable.h"
#include "bst.h"


char tempPath[addressLength];
char name[fileNameLength];


/*/////////////////////////////////////////////////////////////////////////////
~~~~~~~~~~~~~~~~~~~~~~~~~~~~//Function Definitions/~~~~~~~~~~~~~~~~~~~~~~~~~~~
////////////////////////////////////////////////////////////////////////////*/


// Function to add a node as child(directory* new) of given node(directory* parent)//

int addNode(directory* parent, directory* newDir) {

	directory* temp = parent;

	if (temp == NULL)
		temp = head;
	if (temp != NULL && temp->type == 'd') {
		if (temp->child != NULL) {
			newDir->parent = temp;
			newDir->rightSib = temp->child;
			newDir->leftSib = NULL;
			newDir->child = NULL;
			temp->child->leftSib = newDir;
			temp->child = newDir;
			//	printf("Entered %s as child of %s", newDir->name, temp->name); //, findPath(newDir));
		} else {
			temp->child = newDir;
			newDir->parent = temp;
			newDir->rightSib = NULL;
			newDir->leftSib = NULL;
			newDir->child = NULL;
			//	printf("Entered %s as child of %s", newDir->name, temp->name); //, findPath(newDir));
		}
		return 0;
	} //else
	//	printf("Invalid parent\n");
	return 1;
}


// Function to create node in naryTree and initialize it with given values //

directory* creatNode(char *path, char* Name, char fileType, int recursive) {

	char* pathBackUp = (char*) malloc(strlen(path) + 5);
	strcpy(pathBackUp, path);

	//   printf("Creat node : Path is : %s", pathBackUp);
	directory* newDir = NULL;

	iNode* inode = (iNode*) malloc(sizeof(iNode));
	inode->dirName = (char*) malloc(fileNameLength);


	directory* temp;

	if (head != NULL)
		temp = head;
	else
		initializeNAry(1);

	directory* duplicateChecker;
	
	traceIPath(path, inode);
	
	if (inode->parent != NULL)
		temp = inode->parent;

	if (recursive == 1 && inode->dirName != NULL) {
		return temp;
	}

	if (temp->type == 'f') {
	//	printf("PARENT_CANNOT_BE_FILE\n");
		return head;
	}
	while (inode->dirName != NULL && recursive == 0) {
		//  printf("Cur Parent %s, Dir to Add %s", temp->name, inode->dirName);
		newDir = (directory*) malloc(sizeof(directory));
		newDir->name = (char*) malloc(strlen(inode->dirName) + 5);
		strcpy(newDir->name, inode->dirName);
		newDir->type = 'd';
		
		if(addNode(temp, newDir)!=0){
			return NULL;
		}
		newDir->fileDescOffset = updateFileDescriptor(findPath(newDir), inode->dirName, 'd', 0, 0);
		insertFile(newDir->name,newDir->fileDescOffset );
		add_elt(findPath(newDir));
		strcpy(path, pathBackUp);
		//     printf("Path is : %s", path);
		traceIPath(path, inode);
		temp = inode->parent;
	}

	if (Name != NULL) {
		duplicateChecker = temp->child;
		while (duplicateChecker != NULL) { // Check if a dir/file already exists with same name as the one to be created
			if (strcmp(duplicateChecker->name, Name) == 0)
				return NULL;
			duplicateChecker = duplicateChecker->rightSib;
		}
		//     printf("Current Parent %s, Dir to Add %s", temp->name, Name);
		newDir = (directory*) malloc(sizeof(directory));
		//newDir->name = (char*) malloc(strlen(Name));
		newDir->name = (char*) malloc(fileNameLength);
		strcpy(newDir->name, Name);
		newDir->type = fileType;
		if(addNode(temp, newDir)!=0)
		return NULL;
		//printf(":%s:",newDir->name);
		return newDir;
	}
	return temp;
}

// source path will contain address till parent node + name of node to be moved
// dest path will contain path till new parent

int moveNode(char* sourceNodePath, char* destParentPath, char srcType) {

	FILE* updtPtr; // used to update VFS
	FileDescriptor flDesc;

	directory *nodeToMove;
	directory *prevParent;
	directory *newParent;
	directory* duplicateChecker;
	char newName[fileNameLength];
	
	if(strcmp(sourceNodePath,destParentPath)==0)
		return 3;
	
	char* add = (char*) malloc(strlen(destParentPath) + 30); //20 for Name, 1 for / and rest is extra
	char* path = (char*) malloc(strlen(sourceNodePath) + 5);

	iNode* inode = (iNode*) malloc(sizeof(iNode));
	inode->dirName = (char*) malloc(fileNameLength); 
	
	strcpy(add, destParentPath);
	if (strcmp(destParentPath, "/") != 0)
		strcat(add, "/");

	strcpy(path, sourceNodePath);

	if(traceIPath(sourceNodePath, inode)==NULL){
		return 6;
	}
	nodeToMove = inode->parent;
	prevParent = nodeToMove->parent;

	if (nodeToMove->type != srcType)
		return 5; //Source type mismatch

	

	if (strcmp(getFileDirName(path), nodeToMove->name) != 0)
		return 1; // Source do not exists

	free(path);
	path = (char*) malloc(strlen(destParentPath) + 5);
	strcpy(path, destParentPath);

	traceIPath(destParentPath, inode);
	newParent = inode->parent;
	
/*

	if (newParent->type != 'd')
		return 4; //DESTINATION_CANNOT_BE_FILE
		
*/
		if (srcType =='d' && newParent->type != 'd')
			return 4; //DESTINATION_CANNOT_BE_FILE
		else if(srcType =='d' && newParent->type == 'd'){
			strcpy(newName,nodeToMove->name);
			if (strcmp(getFileDirName(path), newParent->name) != 0)
			return 2; // Destination Dir do not exists
		}
		else if (srcType =='f' && newParent->type == 'd'){
			strcpy(newName,getFileDirName(path));
			if(strcmp(newName,newParent->name)==0){
				return 3;
			}
		}
		else if(srcType =='f' && newParent->type == 'f'){
			strcpy(newName,newParent->name);
			if(newParent->parent!=NULL)
			newParent=newParent->parent;
			else
				return 3;
		}
	
/*
	if (strcmp(getFileDirName(path), newParent->name) != 0)
		return 2; // Destination Dir do not exists
*/

	strcat(add, newName);
	duplicateChecker = newParent->child;
	while (duplicateChecker != NULL) { // Check if a dir/file already exists with same name as the one to be created
		if (strcmp(duplicateChecker->name, newName) == 0){
			if(srcType=='d')
			return 3;
			else if(srcType==duplicateChecker->type){
			//	if(duplicateChecker->parent->name,newParent)
			setFileDescriptorFree(duplicateChecker->fileDescOffset);
			setBlockFree(setFileDescriptorFree(duplicateChecker->fileDescOffset),0);
			deleteNode(duplicateChecker);
			break;
			}
			else
				return 3;
		}
		duplicateChecker = duplicateChecker->rightSib;
	}


	//   printf("%s will be moved to %s", nodeToMove->name, newParent->name);

	// de-attach directory from old Parent

	if (nodeToMove->leftSib == NULL)
		prevParent->child = nodeToMove->rightSib;
	else
		nodeToMove->leftSib->rightSib = nodeToMove->rightSib;

	if (nodeToMove->rightSib != NULL)
		nodeToMove->rightSib->leftSib = nodeToMove->leftSib;

	// Attach dirToMove to newParent

	if (newParent->child != NULL)
		newParent->child->leftSib = nodeToMove;

	nodeToMove->rightSib = newParent->child;
	nodeToMove->leftSib = NULL;
	nodeToMove->parent = newParent;
	newParent->child = nodeToMove;

	strcpy(nodeToMove->name,newName);
	
	// Make changes to the address part of file descriptor associated with this node.
	if ((updtPtr = fopen(fileSystemName, "rb+")) == NULL) {
		//printf("FileSystem could not be opened by move file.\n");
		return 4;
	} else {
		fseek(updtPtr, nodeToMove->fileDescOffset, SEEK_SET);
		//       printf("loc %ld ptr %ld\n", nodeToMove->fileDescOffset, ftell(updtPtr));
		fread(&flDesc, sizeof(FileDescriptor), 1, updtPtr);
		// add file name to the end of path till new parent dir

		//      printf("Old Address : %s\n", flDesc.address);
		delete_node(flDesc.address);
		strcpy(flDesc.address, add);
		strcpy(flDesc.name,newName);
		add_elt(flDesc.address);
		//      printf("New Address : %s\n", flDesc.address);
		fseek(updtPtr, -sizeof(FileDescriptor), SEEK_CUR);
		
		fwrite(&flDesc, sizeof(FileDescriptor), 1, updtPtr);

		updateAddress(updtPtr, nodeToMove->child);

		fflush(updtPtr);
		fclose(updtPtr);

		/*
				free(nodeToMove);
				free(prevParent);
				free(newParent);
		 */
		free(duplicateChecker);
		free(add);
		free(path);
		free(inode);
		return 0;
	}


}

int updateAddress(FILE* updtPtr, directory* curDir) {

	FileDescriptor flDesc;
	directory *temp1 = curDir;
	directory *temp2 = curDir;

	if (temp1 != NULL) {

		if (temp1->child != NULL) {
			fseek(updtPtr, temp1->fileDescOffset, SEEK_SET);
			//printf("loc %ld ptr %ld\n", nodeToMove->fileDescOffset, ftell(updtPtr));
			fread(&flDesc, sizeof(FileDescriptor), 1, updtPtr);
			//add file name to the end of path till new parent dir
			//printf("Old Address : %s\n", flDesc.address);
		
			delete_node(flDesc.address);
				strcpy(flDesc.address, findPath(temp1));
				add_elt(flDesc.address);
	
			//printf("New Address : %s\n", flDesc.address);
			fseek(updtPtr, -sizeof(FileDescriptor), SEEK_CUR);
			fwrite(&flDesc, sizeof(FileDescriptor), 1, updtPtr);

			temp2 = temp1->child;
			while (temp2 != NULL) {

				fseek(updtPtr, temp2->fileDescOffset, SEEK_SET);
				//printf("loc %ld ptr %ld\n", nodeToMove->fileDescOffset, ftell(updtPtr));
				fread(&flDesc, sizeof(FileDescriptor), 1, updtPtr);
				//add file name to the end of path till new parent dir
				//printf("Old Address : %s\n", flDesc.address);
				delete_node(flDesc.address);
				strcpy(flDesc.address, findPath(temp2));
				add_elt(flDesc.address);
			
				//printf("New Address : %s\n", flDesc.address);
				fseek(updtPtr, -sizeof(FileDescriptor), SEEK_CUR);
				fwrite(&flDesc, sizeof(FileDescriptor), 1, updtPtr);

				temp2 = temp2->rightSib;
			}
		}
		//if(temp1->type!='f')
		updateAddress(updtPtr, temp1->child);
		updateAddress(updtPtr, temp1->rightSib);

	}
	return 0;
}

int deleteNode(directory *curDir) {

	directory *parentDir = curDir->parent;

	if (parentDir != NULL) {
		if (strcmp(parentDir->child->name, curDir->name) == 0)
			parentDir->child = curDir->rightSib;
		else
			curDir->leftSib->rightSib = curDir->rightSib; //Reassigning pointers of doubly linked list

		if (curDir->rightSib != NULL)
			curDir->rightSib->leftSib = curDir->leftSib;

		curDir->rightSib = NULL;
		curDir->leftSib = NULL;
		curDir->parent = NULL;
		
	}else 
		head=NULL;
	
	return 0;
}

//Finds the directory path(address) from root to given directory node, traversing backward//

char* findPath(directory* node) {
	char path[500];
	strcpy(tempPath, "");
	directory* temp = node;
	while (temp != NULL) {
		if (temp->parent != NULL && temp->parent != head)
			strcpy(path, "/");
		else
			strcpy(path, "");
		strcat(path, temp->name);
		strcat(path, tempPath);
		strcpy(tempPath, path);
		temp = temp->parent;
	}
	return tempPath;
}



//Function to traverse directory structure (sibling first)//

void traverse(directory *head) {

	directory *temp1 = head;
	directory *temp2 = head;

	if (temp1 != NULL) {

		if (temp1->child != NULL) {
			printf("List of Directories/files inside : %s", temp1->name);
			printf("\t\t\t");

			temp2 = temp1->child;
			while (temp2 != NULL) {
				printf("%s # ", temp2->name);
				temp2 = temp2->rightSib;
			}
		}

		traverse(temp1->child);
		traverse(temp1->rightSib);

	}
}


//check if the given path exists and returns the pointer to parent of last available node//

directory* tracePath(directory *head, char* path) {
	directory * temp = head;

	char *strtok(char *str1, const char *str2);

	char delims[] = "/";
	char *result = NULL;
	result = strtok(path, delims);

	while (result != NULL && temp != NULL) {
		if (strcmp(temp->name, result) == 0) {
			if (temp->child != NULL)
				temp = temp->child;
			else {
				result = strtok(NULL, delims);
				break;
			}
			result = strtok(NULL, delims);

		} else {
			if (temp->rightSib != NULL)
				temp = temp->rightSib;
			else {
				temp = temp->parent;
				break;
			}
		}
	}
	//  printf("Dir reached %s", temp->name);
	if (result != NULL) {
		//      printf("Sending %s", temp->name);
		return temp;
	} else {
		if (temp != NULL && temp->parent != NULL) {
			//          printf("Sending %s", temp->parent->name);
			return temp->parent;
		} else
			return NULL;
	}
}



//check if the given path exists and returns the structure iNode, containing //
//pointer to last available node as parent, and name of found node.          //

directory * traceIPath(char* path, iNode* inode) {

	if (mountStatus == unMounted || head == NULL) {
	//	printf("%d %p\n",mountStatus,head);
		return NULL;
	}
	directory * temp;
	if (head->child != NULL)
		temp = head->child;
	else
		temp = head;

	if (strcmp(path, "/") == 0) {
		inode->parent = head;
		inode->dirName = NULL;
	} else {

		// printf("Path %s:\n",path);

		char *strtok(char *str1, const char *str2);

		char delims[] = "/";
		char *result = strtok(path, delims);



		while (result != NULL && temp != NULL) {
		//    printf(":%s:%s:\n",temp->name,result);
			if (strcmp(temp->name, result) == 0) {
				result = strtok(NULL, delims);
				if (result != NULL && temp->child != NULL)
					temp = temp->child;
				else
					break;
			} else {
				if (temp->rightSib != NULL)
					temp = temp->rightSib;
				else {
					if (temp != head)
						temp = temp->parent;
					break;
				}
			}
		}

		inode->dirName = result;
		// printf("%s:%s:\n",temp->name,result);

		if (result != NULL) {
			//    printf("Intermediate path found");
			inode->parent = temp;
		} else {
			//       printf("Complete Path found");
			inode->parent = temp;
		}
		// if (inode->parent != NULL){
		//       printf("Found Parent %s, directory to add %s", inode->parent->name, inode->dirName);
		//}
	}
	
	return temp;
}

// initialize head pointer, for i = 1, it will write node to VFS

int initializeNAry(int i) {

	head = (directory *) malloc(sizeof(directory));
	head->name = (char*) malloc(fileNameLength);
	strcpy(head->name, "/");
	head->rightSib = NULL;
	head->leftSib = NULL;
	head->parent = NULL;
	head->child = NULL;
	head->type = 'd';
	if (i == 1) {
		head->fileDescOffset = updateFileDescriptor("/", "/", 'd', 0, 0);
		//head = NULL;
	}
	return 0;
}


//function to mount the n-ary

int mountNAry(FileDescriptor *fDes, long offset) {

	//   printf("Mounting NAry");
	char* path = (char*) malloc(strlen(fDes->address) + 5);
	strcpy(path, fDes->address);

	//   printf("Path is : %s", path);
	directory* newDir = NULL;

	iNode* inode = (iNode*) malloc(sizeof(iNode));
	inode->dirName = (char*) malloc(fileNameLength);

	directory* temp = head;

	if (head == NULL)
		initializeNAry(0);

	traceIPath(path, inode);
		
		
	if (inode->parent != NULL)
		temp = inode->parent;

	// if(temp!=head)
	while (inode->dirName != NULL) {

		//      printf("Cur Parent %s, Dir to Add %s", temp->name, inode->dirName);
		newDir = (directory*) malloc(sizeof(directory));
		newDir->name = (char*) malloc(fileNameLength);
		strcpy(newDir->name, inode->dirName);
		newDir->type = 'd';
		addNode(temp, newDir);
		strcpy(path, fDes->address);
		//       printf("Path is : %s", path);
		traceIPath(path, inode);
		temp = inode->parent;
	}
	temp->type = fDes->type;
	temp->fileDescOffset = offset;

	//printf("\tNode with name : %s represent fd at %ld of type %c\n", temp->name, temp->fileDescOffset, temp->type);
	return 0;
}

char* getFileDirName(char* path) {
	char *strtok(char *str1, const char *str2);
	char delims[] = "/";

	if (strcmp(path, "/") == 0)
		return path;

	char *result = strtok(path, delims);

	while (result != NULL) {
		strcpy(name, result);
		result = strtok(NULL, delims);
	}
	//   printf("Name is : %s", name);
	return name;
}

int invalidChar(char *name, int mode) {
	char *temp = (char*) malloc(strlen(name) + 5);
	strcpy(temp, name);
	if (mode == 0) {
		temp = strtok(temp, "/");
	} else {
		if (*temp != '/')
			return 1;
		//	temp = strtok(temp, " ~`!@#$%^*+=\\|:;\"'<>?,");
	}
	if (temp!=NULL && strcmp(temp, name) == 0)
		return 0;
	else
		return 1;
}

int checkHead() {

	if (head == NULL)
		return 1;
	else
		return 0;
}

int setHead(directory* temp) {
	head = temp;
	return 0;
}
